﻿#include "computer.h"
#include "control.h"
#include <QDebug>
#include <QTime>

Computer::Computer(ChessPiece r[32], bool &isRed): _original_s(r),
    _isRedTurn(isRed) {
    init();
}

Computer::~Computer() {
    if(_s) {
        delete[] _s;
    }
}

void Computer::init() {
    // 为所有棋子引用的拷贝分配内存
    _s = new ChessPiece[32];
    // 为computer构造棋子控制类
    _pCtrl = new Control(_isRedTurn);
    // 初始化分值表
    _scoreTable = {{ChessPiece::JIANG, 15000}, {ChessPiece::CHE, 120}, {ChessPiece::MA, 50}, {ChessPiece::XIANG, 10},
        {ChessPiece::SHI, 10}, {ChessPiece::PAO, 65}, {ChessPiece::BING, 20}
    };
    // 博弈树层数
    _level = 3;
}

// widget发送的开始电脑计算最优走棋信后的槽函数
void Computer::startComputerSlot() {
    _s = new ChessPiece[32];
    memcpy(_s, _original_s, sizeof(ChessPiece) * 32);
    Step *step = getBestMove();
    emit sendComputerBestStep(step);
}

// 获取最优走棋
Step *Computer::getBestMove() {
    QVector<Step *> allSteps;
    getAllPossibleMove(allSteps);
    int maxScore = INT_MIN;
    Step *bestStep = nullptr;
    while(allSteps.count()) {
        Step *step = allSteps.last();
        allSteps.removeLast();
        fakeMove(step);                                         // 假使移动一步
        int score = getMinScore(this->_level - 1, maxScore);    // 模拟下一步人走棋,在每一种可能中获取最小值
        refakeMove(step);                                       // 再将移动的棋子放回原处
        // 比较分值,找到最大的,将这一分值对应的Step记录
        if(score > maxScore) {
            if(bestStep) {
                delete bestStep;
            }
            maxScore = score;
            bestStep = step;
        } else {
            delete step;
        }
    }
    return bestStep;
}

// 获取所有可以走的路径
void Computer::getAllPossibleMove(QVector<Step *> &steps) {
    int startId = 0, endId = 16;
    if(_isRedTurn) {
        startId = 16;
        endId = 32;
    }
    memset(_boradTable, -1, sizeof(_boradTable));
    for(int i = 0; i < 32; ++i) {
        if(!_s[i]._isDead) {
            _boradTable[_s[i]._row][_s[i]._col] = i;
        }
    }
    // 检索每一个棋子可以走的路径
    for(qint8 i = startId; i < endId; ++i) {
        if(_s[i]._isDead) {
            continue;
        }
        for(qint8 row = 0; row <= 9; ++row) {
            for(qint8 col = 0; col <= 8; ++col) {
                int killId = _boradTable[row][col];
                if(killId != -1 && _s[i]._isRed == _s[killId]._isRed) {            // 如果同色,跳出
                    continue;
                }
                if(_pCtrl->canMove(_s, i, row, col, killId)) {                         // 如果这一步可以走,保存
                    steps.append(_pCtrl->saveStep(_s, i, killId, row, col));
                }
            }
        }
    }
}

// 假设移动一步
void Computer::fakeMove(Step *&step) {
    if(step) {
        _pCtrl->moveChess(_s, step->_moveId, step->_killId, step->_rowTo, step->_colTo);
    }
}

// 假设移动回去
void Computer::refakeMove(Step *&step) {
    if(step) {
        _pCtrl->moveChess(_s, step->_moveId, -1, step->_rowFrom, step->_colFrom);
        _pCtrl->reSaveChess(_s, step->_killId);
    }
}

// 计算当前局势分
int Computer::calcScore() {
    int redScore = 0;
    int blackScore = 0;
    for(qint8 i = 0; i < 32; ++i) {
        if(_s[i]._isDead) {
            continue;
        }
        if(_s[i]._isRed) {
            redScore += _scoreTable.value(_s[i]._type);
        } else {
            blackScore += _scoreTable.value(_s[i]._type);
        }
    }
    return blackScore - redScore;
}

// 最大最小值算法: 获取最小值
int Computer::getMinScore(qint8 level, int curMaxScore) {
    if(!level) {
        return calcScore();
    }
    QVector<Step *> allSteps;
    getAllPossibleMove(allSteps);
    int minScore = INT_MAX;
    while(allSteps.count()) {
        Step *step = allSteps.last();
        allSteps.removeLast();
        fakeMove(step);             // 假使移动一步,计算当前局面分
        int score = getMaxScore(level - 1, minScore);
        refakeMove(step);           // 再将移动的棋子放回原处
        delete step;
        // 优化剪枝
        if(score <= curMaxScore) {
            while(allSteps.count()) {
                Step *step = allSteps.last();
                allSteps.removeLast();
                delete step;
            }
            return score;
        }
        // 比较分值,找到最小的,将这一分值对应的Step记录
        if(score < minScore) {
            minScore = score;
        }
    }
    return minScore;
}

// 最大最小值算法: 获取最大值
int Computer::getMaxScore(qint8 level, int curMinScore) {
    if(!level) {
        return calcScore();
    }
    QVector<Step *> allSteps;
    getAllPossibleMove(allSteps);
    int maxScore = INT_MIN;
    while(allSteps.count()) {
        Step *step = allSteps.last();   // 从向量中取出一个元素
        allSteps.removeLast();
        fakeMove(step);                 // 假使移动一步,计算当前局面分
        int score = getMinScore(level - 1, maxScore);
        refakeMove(step);               // 再将移动的棋子放回原处
        delete step;
        // 优化剪枝
        if(score >= curMinScore) {
            while(allSteps.count()) {
                Step *step = allSteps.last();
                allSteps.removeLast();
                delete step;
            }
            return score;
        }
        // 比较分值,找到最大的,将这一分值对应的Step记录
        if(score > maxScore) {
            maxScore = score;
        }
    }
    return maxScore;
}

